#pragma once

#include <iostream>
#include <string>
#include <unordered_map>
#include "Connection.hpp"
#include "Epoller.hpp"

// 对已经就绪的事件进行通知，对connection进行管理
// 这个 TcpServer 就是 Reactor(反应堆)
class Reactor
{
    static const int gnum = 64;
    static const int TIMEOUT = -1;
public:
    Reactor()
        :_isrunning(false)
    {}

    void AddConnection(int fd, uint32_t events, func_t recver, func_t sender, func_t excepter)
    {
        // 构建 connection，设置关心事件和回调函数，设定 self
        Connection* conn = new Connection(fd);
        conn->SetEvents(events);
        
        conn->Register(recver, sender, excepter);
        conn->SetSelf(this);

        // 向内核设定对 fd 的关心
        _epoller.AddEvent(conn->GetSockfd(), conn->GetEvents());

        _connections.insert({fd, conn});
        LOG(INFO, "Add Connection success\n");
    }

    // 打开设置对文件描述符读或者写的关心
    void EnableReadWrite(int sockfd, bool readable, bool writeable)
    {
        uint32_t events = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0);
        if(IsConnectionExist(sockfd))
        {
            _connections[sockfd]->SetEvents(events);
            _epoller.ModifyEvent(sockfd, events);
        }
    }

    bool IsConnectionExist(int sockfd)
    {
        auto iter = _connections.find(sockfd);
        return iter != _connections.end();
    }

    void RemoveConnection(int fd)
    {
        if(!IsConnectionExist(fd)) return;
        // 1、去掉epoll对该文件描述符的关心
        _epoller.DelEvents(fd);
        
        // 2、关闭文件描述符
        _connections[fd]->Close();
        // 3、释放connection
        delete _connections[fd];
        _connections.erase(fd);
    }

    void LoopOnce(int timeout)
    {
        int n = _epoller.Wait(_revs, gnum, timeout);
        LOG(INFO, "epoll_wait: n = %d\n", n);
        for(int i = 0; i < n; ++i)
        {
            int fd = _revs[i].data.fd;
            uint32_t event = _revs[i].events;

            // EPOLLHUP: 对方把链接关了
            // 把异常转化为读写就绪
            if(event & EPOLLHUP) event |= (EPOLLIN | EPOLLOUT);
            if(event & EPOLLERR) event |= (EPOLLIN | EPOLLOUT);

            if(event & EPOLLIN)
            {
                if(IsConnectionExist(fd) && _connections[fd]->_recver != nullptr)
                {
                    LOG(INFO, "callback recv function...\n");
                    _connections[fd]->_recver(_connections[fd]);
                }
            }
            if(event & EPOLLOUT)
            {
                if(IsConnectionExist(fd) && _connections[fd]->_sender != nullptr)
                {
                    _connections[fd]->_sender(_connections[fd]);
                }
            }
        }
    }

    // 读写事件的派发
    void Dispatcher()
    {
        _isrunning = true;
        while(_isrunning)
        {
            LoopOnce(TIMEOUT);
            CheckAllConnections();
        }
        _isrunning = false;
    }

    void CheckAllConnections()
    {
        std::cout << "-----------------------------------" << std::endl;
        for(auto pair : _connections)
        {
            uint32_t events = pair.second->GetEvents();
            printf("fd: %d, events: ", pair.first);
            if((events & EPOLLIN) && (events & EPOLLOUT))
                printf("EPOLLIN | EPOLLOUT");
            else if(events & EPOLLIN) 
                printf("EPOLLIN");
            else if(events & EPOLLOUT) 
                printf("EPOLLOUT");
            printf("\n");
        }
        std::cout << "-----------------------------------" << std::endl;
    }


private:
    std::unordered_map<int, Connection*> _connections;// sockfd -> connection*
    Epoller _epoller;

    epoll_event _revs[gnum];

    bool _isrunning;
};